package thread;

import java.util.Scanner;

public class Demo19 {
    //wait等待 和 notify通知
    public static void main1(String[] args) throws InterruptedException {
        //我们对于这个 wait等待来说呢，我们要想要使用的话，我们需要 先加锁，wait等待才能解锁等待

        Object object = new Object();
        System.out.println("wait 等待之前");
        synchronized (object) {//加锁

            object.wait();//解锁
            //再次加锁
        }
        System.out.println("wait 等待之后");
    }

    public static void main2(String[] args) {
        //看看对于 notify 怎么使用
        //虽然一般来说 对于 notify 这个关键词呢不需要使用锁的，但是在 Java 中是强制使用锁的需要
        Object locker = new Object();
        Object locker2 = new Object();
        //这里我们也要注意，对于 wait 和 notify 的对象和加锁对象都必须保持一致，不然就不会正常执行
        Thread t1 = new Thread(() -> {
           try{
               Thread.sleep(10000);
               //这里添加 sleep 会导致 notify 在 wait 执行之前执行导致wait 接收不到notify传出来的通知，导致不会进行再次加锁
               System.out.println("wait 等待之前");
               synchronized (locker) {
                   locker.wait();
               }
               System.out.println("wait 等待之后");
           }catch (InterruptedException e) {
               throw new RuntimeException();
           }
        });

        Thread t2 = new Thread(() -> {
           Scanner scanner = new Scanner(System.in);
           System.out.println("输入一个值 进行通知操作，来唤醒 t1");
           scanner.nextInt();

           synchronized (locker) {
               locker.notify();
            }
        });

        t1.start();
        t2.start();
    }

    //我们的 wait() 里面是可以添加数值的，就如同 join() 的等待相似。
    public static void main3(String[] args) {
        Object locker = new Object();

        Thread t1 = new Thread(() -> {
           try {
               System.out.println("t1 的 wait 之前");
               synchronized (locker) {
                   locker.wait(10000);//等待10s 后如果没有 通知 ，那么也会退出wait
               }
               System.out.println("t1 的 wait 之后");
           }catch (InterruptedException e){
               throw new RuntimeException();
            }
        });

        Thread t2 = new Thread(() -> {
           try {
               System.out.println("t2 的 wait 之前");
               synchronized (locker) {
                   locker.wait(10000);
               }
               System.out.println("t2 的 wait 之后");
           }catch (InterruptedException e) {
               throw new RuntimeException();
           }
        });

        Thread t3 = new Thread(() -> {
           Scanner scanner = new Scanner(System.in);
           System.out.println("输入任意一个数，唤醒一个线程");
           scanner.nextInt();

//           synchronized (locker) {
//               //这样呢会随机的通知其中的一个线程
//               locker.notify();
//           }
//
//           System.out.println("输入任意一个数，唤醒另一个线程");
//           scanner.nextInt();
//
//           synchronized (locker) {
//               locker.notify();
//           }

           //对于这个上面的多个线程的通知呢，我们可以使用 notifyAll 这个方法来完成
           //但是对于这个方法呢，并不是这些 wait 一起接收到通知的，而是他们需要进行竞争对于和这个通知的先后
           synchronized (locker) {
               locker.notifyAll();
           }
        });

        t1.start();
        t2.start();
        t3.start();
    }


    //我们来看一个例题，我们打印10次顺序为 "ABC" 这个顺序的字符串
    public static void main(String[] args) throws InterruptedException {
        Object locker1 = new Object();
        Object locker2 = new Object();
        Object locker3 = new Object();

        //我们需要用到 wait notify 这些方法的使用
        Thread t1 = new Thread(() -> {
           try {
               for (int i = 0; i < 10; i++) {
                   synchronized (locker1) {
                       locker1.wait();
                   }
                   System.out.print("A");
                   synchronized (locker2) {
                       locker2.notify();
                   }
               }
           }catch (InterruptedException e) {
               throw new RuntimeException();
           }
        });

        Thread t2 = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    synchronized (locker2) {
                        locker2.wait();
                    }
                    System.out.print("B");
                    synchronized (locker3) {
                        locker3.notify();
                    }
                }
            }catch (InterruptedException e) {
                throw new RuntimeException();
            }
        });

        Thread t3 = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    synchronized (locker3) {
                        locker3.wait();
                    }
                    System.out.println("C");
                    synchronized (locker1) {
                        locker1.notify();
                    }
                }
            }catch (InterruptedException e) {
                throw new RuntimeException();
            }
        });

        t1.start();
        t2.start();
        t3.start();

        //这个时候我们对于上面的 三个线程的循环打印呢，我们就需要一个 动力，迫使t1 先进行打印A，我们需要同时t1的wait执行
        Thread.sleep(1000);//这里是为了保证 t1 和 t2 和 t3 都是进行了 wait 进行等待阶段
        synchronized (locker1) {
            locker1.notify();
        }
    }
}
